package com.mlethe.library.keyboard;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

import androidx.core.content.ContextCompat;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

/**
 * 自定义键盘view
 *
 * @author Mlethe
 */
public class KeyboardView extends android.inputmethodservice.KeyboardView {

    private final Rect mBounds = new Rect();
    private final Paint mPaint = new Paint();
    private final int specialTextColor;
    private final int delId, delDownId, shiftId, shiftDownId, shiftUpperId, shiftUpperDownId, specialId, specialDownId;
    private Drawable del, delDown, shift, shiftDown, shiftUpper, shiftUpperDown, special, specialDown;

    private Keyboard[] mKeyboards;

    private EditText mEditText;

    private SparseArray<KeyStyle> mSpecialData = new SparseArray<>();

    private OnAttachStateChangeListener mOnAttachStateChangeListener;

    public KeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.KeyboardView);
        delId = array.getResourceId(R.styleable.KeyboardView_delKeyDrawable, 0);
        delDownId = array.getResourceId(R.styleable.KeyboardView_delKeyDownDrawable, 0);
        shiftId = array.getResourceId(R.styleable.KeyboardView_shiftKeyDrawable, 0);
        shiftDownId = array.getResourceId(R.styleable.KeyboardView_shiftKeyDownDrawable, 0);
        shiftUpperId = array.getResourceId(R.styleable.KeyboardView_shiftUpperKeyDrawable, 0);
        shiftUpperDownId = array.getResourceId(R.styleable.KeyboardView_shiftUpperKeyDownDrawable, 0);
        specialTextColor = array.getColor(R.styleable.KeyboardView_specialKeyTextColor, Color.WHITE);
        specialId = array.getResourceId(R.styleable.KeyboardView_specialKeyBackground, 0);
        specialDownId = array.getResourceId(R.styleable.KeyboardView_specialKeyDownBackground, 0);
        array.recycle();
        mPaint.setAntiAlias(true);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setTypeface(Typeface.DEFAULT_BOLD);
        mPaint.setStyle(Paint.Style.FILL);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mOnAttachStateChangeListener != null) {
            mOnAttachStateChangeListener.onViewAttachedToWindow(this);
        }
        if (del == null) {
            del = createDrawable(delId);
        }
        if (delDown == null) {
            delDown = createDrawable(delDownId);
        }
        if (shift == null) {
            shift = createDrawable(shiftId);
        }
        if (shiftDown == null) {
            shiftDown = createDrawable(shiftDownId);
        }
        if (shiftUpper == null) {
            shiftUpper = createDrawable(shiftUpperId);
        }
        if (shiftUpperDown == null) {
            shiftUpperDown = createDrawable(shiftUpperDownId);
        }
        if (special == null) {
            special = createDrawable(specialId);
        }
        if (specialDown == null) {
            specialDown = createDrawable(specialDownId);
        }
    }

    /**
     * 创建Drawable
     *
     * @param resId
     * @return
     */
    private Drawable createDrawable(int resId) {
        if (resId == 0) {
            return null;
        }
        return ContextCompat.getDrawable(getContext(), resId);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        try {
            List<Keyboard.Key> keys = getKeyboard().getKeys();
            for (Keyboard.Key key : keys) {
                if (key.codes[0] == Keyboard.KEYCODE_DELETE) {
                    // 删除按钮
                    if (key.pressed) {
                        // 按下
                        if (delDown != null) {
                            delDown.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                            delDown.draw(canvas);
                        }
                    } else {
                        if (del != null) {
                            del.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                            del.draw(canvas);
                        }
                    }
                } else if (key.codes[0] == Keyboard.KEYCODE_SHIFT) {
                    if (key.pressed) {
                        if (WordKeyboard.isUpper) {
                            if (shiftUpperDown != null) {
                                shiftUpperDown.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                                shiftUpperDown.draw(canvas);
                            } else {
                                if (shiftUpper != null) {
                                    shiftUpper.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                                    shiftUpper.draw(canvas);
                                }
                            }
                        } else {
                            if (shiftDown != null) {
                                shiftDown.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                                shiftDown.draw(canvas);
                            } else {
                                if (shift != null) {
                                    shift.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                                    shift.draw(canvas);
                                }
                            }
                        }
                    } else {
                        if (WordKeyboard.isUpper) {
                            if (shiftUpper != null) {
                                shiftUpper.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                                shiftUpper.draw(canvas);
                            }
                        } else {
                            if (shift != null) {
                                shift.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                                shift.draw(canvas);
                            }
                        }
                    }
                } else if (key.codes[0] == -2 || key.codes[0] == 90001 || key.codes[0] == 100860) {
                    if (key.pressed) {
                        if (specialDown != null) {
                            specialDown.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                            specialDown.draw(canvas);
                        }
                    } else {
                        if (special != null) {
                            special.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                            special.draw(canvas);
                        }
                    }
                    mBounds.setEmpty();
                    mPaint.setColor(specialTextColor);
                    drawText(canvas, key, this, mBounds, mPaint);
                } else {
                    KeyStyle keyStyle = mSpecialData.get(key.codes[0]);
                    if (keyStyle != null) {
                        if (key.pressed) {
                            Drawable drawable = createDrawable(keyStyle.downBackgroundId);
                            if (drawable != null) {
                                drawable.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                                drawable.draw(canvas);
                            }
                        } else {
                            Drawable drawable = createDrawable(keyStyle.backgroundId);
                            if (drawable != null) {
                                drawable.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                                drawable.draw(canvas);
                            }
                        }
                        if (keyStyle.isShowText) {
                            mBounds.setEmpty();
                            mPaint.setColor(Color.parseColor(keyStyle.textColor));
                            drawText(canvas, key, this, mBounds, mPaint);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 绘制文字
     *
     * @param canvas
     * @param key
     */
    protected void drawText(Canvas canvas, Keyboard.Key key, KeyboardView keyboardView, Rect bounds, Paint paint) {
        try {
            if (key.label != null) {
                String label = key.label.toString();
                Field field = android.inputmethodservice.KeyboardView.class.getDeclaredField("mLabelTextSize");
                field.setAccessible(true);
                int labelTextSize = (int) field.get(keyboardView);
                paint.setTextSize(labelTextSize);
                if (label.length() > 1 && key.codes.length < 2) {
                    paint.setTypeface(Typeface.DEFAULT_BOLD);
                } else {
                    paint.setTypeface(Typeface.DEFAULT);
                }
                paint.getTextBounds(key.label.toString(), 0, key.label.toString().length(), bounds);
                canvas.drawText(key.label.toString(), key.x + (key.width / 2), (key.y + key.height / 2) + bounds.height() / 2, paint);
            } else if (key.icon != null) {
                key.icon.setBounds(key.x + (key.width - key.icon.getIntrinsicWidth()) / 2, key.y + (key.height - key.icon.getIntrinsicHeight()) / 2,
                        key.x + (key.width - key.icon.getIntrinsicWidth()) / 2 + key.icon.getIntrinsicWidth(), key.y + (key.height - key.icon.getIntrinsicHeight()) / 2 + key.icon.getIntrinsicHeight());
                key.icon.draw(canvas);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mOnAttachStateChangeListener != null) {
            mOnAttachStateChangeListener.onViewDetachedFromWindow(this);
        }
        destroyKeyboard();
        if (mBounds != null) {
            mBounds.setEmpty();
        }
        mSpecialData.clear();
        mEditText = null;
        del = null;
        delDown = null;
        shift = null;
        shiftDown = null;
        shiftUpper = null;
        shiftUpperDown = null;
        special = null;
        specialDown = null;
    }

    /**
     * 释放键盘
     */
    private void destroyKeyboard() {
        if (mKeyboards != null && mKeyboards.length > 0) {
            for (Keyboard keyboard : mKeyboards) {
                keyboard.destroy();
            }
            mKeyboards = null;
        }
    }

    public EditText getEditText() {
        return mEditText;
    }

    /**
     * 设置View状态改变监听事件
     *
     * @param onAttachStateChangeListener
     * @return
     */
    public KeyboardView setOnAttachStateChangeListener(OnAttachStateChangeListener onAttachStateChangeListener) {
        this.mOnAttachStateChangeListener = onAttachStateChangeListener;
        return this;
    }

    /**
     * 初始化
     *
     * @param keyboards
     */
    public KeyboardView setKeyboards(Keyboard... keyboards) {
        destroyKeyboard();
        this.mKeyboards = keyboards;
        if (mKeyboards != null && mKeyboards.length > 0) {
            for (Keyboard keyboard : mKeyboards) {
                keyboard.setKeyboardView(this);
            }
            Keyboard keyboard = mKeyboards[0];
            if (keyboard.isRandom()) {
                // 随机键盘
                randomKeyboard(keyboard);
            }
            setEnabled(true);
            setKeyboard(keyboard);
        }
        return this;
    }

    /**
     * 设置键盘是否可以预览
     *
     * @param previewEnabled
     */
    @Override
    public void setPreviewEnabled(boolean previewEnabled) {
        super.setPreviewEnabled(previewEnabled);
    }

    /**
     * 设置键盘
     *
     * @param keyboard
     */
    public KeyboardView setKeyboard(Keyboard keyboard) {
        super.setKeyboard(keyboard);
        setOnKeyboardActionListener(keyboard);
        return this;
    }

    /**
     * 隐藏软键盘
     *
     * @param editText
     */
    private void hideSoftInput(EditText editText) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
            try {
                Class<EditText> cls = EditText.class;
                Method setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class);
                setShowSoftInputOnFocus.setAccessible(true);
                setShowSoftInputOnFocus.invoke(editText, false);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            editText.setInputType(InputType.TYPE_NULL);
        }
        // 如果软键盘已经显示，则隐藏
        InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
    }

    /**
     * 隐藏键盘
     */
    public void hiddenKeyboard() {
        this.mEditText = null;
        if (getVisibility() == View.VISIBLE) {
            setVisibility(View.GONE);
        }
    }

    /**
     * 显示键盘
     *
     * @param editText
     */
    public void showKeyboard(EditText editText) {
        this.mEditText = editText;
        if (mEditText != null) {
            if (!editText.hasFocus()) {
                editText.setFocusable(true);
                editText.setFocusableInTouchMode(true);
                editText.requestFocus();
            }
            hideSoftInput(editText);
            if (getVisibility() != View.VISIBLE) {
                setVisibility(View.VISIBLE);
            }
        }
    }

    /**
     * 切换键盘
     *
     * @param clazz 需要键盘的类型
     */
    public void switchKeyboard(Class<? extends Keyboard> clazz) {
        if (mKeyboards != null) {
            for (Keyboard keyboard : mKeyboards) {
                if (keyboard.getClass().equals(clazz)) {
                    if (keyboard.isRandom()) {
                        // 随机键盘
                        randomKeyboard(keyboard);
                    }
                    setKeyboard(keyboard);
                    return;
                }
            }
        }
    }

    /**
     * 设置key的样式
     *
     * @param keyCode
     * @param keyStyle
     */
    public KeyboardView putKeyStyle(int keyCode, KeyStyle keyStyle) {
        mSpecialData.put(keyCode, keyStyle);
        return this;
    }

    /**
     * 随机键盘
     *
     * @param keyboard
     */
    private void randomKeyboard(Keyboard keyboard) {
        List<Keyboard.Key> keyList = keyboard.getKeys();
        // 查找出数字键和字母键
        List<Keyboard.Key> newKeyList = new ArrayList<>();
        for (int i = 0; i < keyList.size(); i++) {
            if (!TextUtils.isEmpty(keyList.get(i).label) && keyboard.isRandom(keyList.get(i).label)) {
                newKeyList.add(keyList.get(i));
            }
        }
        // 数组长度
        int count = newKeyList.size();
        // 结果集
        List<KeyModel> resultList = new ArrayList<>();
        // 用一个LinkedList作为中介
        LinkedList<KeyModel> temp = new LinkedList<>();
        // 初始化temp
        for (int i = 0; i < count; i++) {
            Keyboard.Key key = newKeyList.get(i);
            temp.add(new KeyModel(key.codes[0], key.label.toString()));
        }
        // 取数
        Random rand = new Random();
        for (int i = 0; i < count; i++) {
            int num = rand.nextInt(count - i);
            resultList.add(new KeyModel(temp.get(num).getCode(), temp.get(num).getLable()));
            temp.remove(num);
        }
        for (int i = 0; i < count; i++) {
            newKeyList.get(i).label = resultList.get(i).getLable();
            newKeyList.get(i).codes[0] = resultList.get(i).getCode();
        }
        resultList.clear();
        newKeyList.clear();
    }

    private static class KeyModel {
        private int code;
        private String lable;

        public KeyModel(int code, String lable) {
            this.code = code;
            this.lable = lable;
        }

        public int getCode() {
            return code;
        }

        public String getLable() {
            return lable;
        }
    }

    public static class KeyStyle {

        /**
         * 是否显示文字：true 显示，false 不显示
         */
        private boolean isShowText;

        /**
         * key 文字颜色
         */
        private String textColor;

        /**
         * key 背景图片资源ID
         */
        private int backgroundId;

        /**
         * key 按下背景图片资源ID
         */
        private int downBackgroundId;

        public KeyStyle(String textColor, int backgroundId, int downBackgroundId) {
            this.isShowText = true;
            this.textColor = textColor;
            this.backgroundId = backgroundId;
            this.downBackgroundId = downBackgroundId;
        }

        public KeyStyle(int backgroundId, int downBackgroundId) {
            this.isShowText = false;
            this.backgroundId = backgroundId;
            this.downBackgroundId = downBackgroundId;
        }
    }
}
